package main

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"chatroom/shared"

	amqp "github.com/rabbitmq/amqp091-go"
)

const (
	serverName = "chatroom-server"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	ch, err := conn.Channel()
	if err != nil {
		panic(err)
	}
	defer ch.Close()

	client := NewClient()

	q, err := ch.QueueDeclare(
		client.Id,
		false,
		true,
		false,
		false,
		nil,
	)

	if err != nil {
		panic(err)
	}

	// Queue need to bind 2 exchanges
	ch.QueueBind(q.Name, "", "amq.fanout", false, nil)
	msgs, err := ch.Consume(
		q.Name,
		"",
		true,
		false,
		false,
		false,
		nil,
	)
	if err != nil {
		panic(err)
	}

	//Receive messages from server
	go func() {
		for msg := range msgs {
			cmd := shared.CommandMsg{}
			if err := json.Unmarshal(msg.Body, &cmd); err != nil {
				log.Printf("Error unmarshalling message: %v", err)
				continue
			}
			if cmd.Command == "newchatmsg" {
				var resp shared.ChatMsgResponse
				if err := json.Unmarshal([]byte(cmd.Body), &resp); err != nil {
					log.Printf("Error unmarshalling message: %v", err)
					continue
				}
				if resp.ClientId == client.Id {
					continue
				}
				timeObj := time.Unix(resp.TimeStamp, 0)
				log.Printf("Received message: %s from %s at %s", resp.Message, resp.ClientName, timeObj.Format("2006-01-02 15:04:05"))
			} else if cmd.Command == "heartbeatresp" {
				log.Printf("%s", cmd.Body)
			}
		}
	}()

	// Send heartbeat to server every 15 seconds
	go func(ctx context.Context, ch *amqp.Channel, client *Client) {
		ticker := time.NewTicker(15 * time.Second)
		defer ticker.Stop()

		SendHeartbeat(ch, &shared.Heartbeat{
			ClientId:   client.Id,
			ClientName: client.Name,
			TimeStamp:  time.Now().Unix(),
		})

		for {
			select {
			case <-ticker.C:
				SendHeartbeat(ch, &shared.Heartbeat{ClientId: client.Id,
					ClientName: client.Name,
					TimeStamp:  time.Now().Unix()})
			case <-ctx.Done():
				return
			}
		}

	}(ctx, ch, client)
	fmt.Println("Waiting for messages...")
	var input string
	reader := bufio.NewReader(os.Stdin)
	for {
		input, _ = reader.ReadString('\n')
		input = strings.TrimSpace(input)
		if input == "exit" {
			break
		} else {
			// Send message to server
			SendChatmsg(ch, &shared.ChatMsg{
				ClientId:  client.Id,
				Message:   input,
				TimeStamp: time.Now().Unix(),
			})
		}
	}
}

func SendHeartbeat(ch *amqp.Channel, h *shared.Heartbeat) error {
	body, err := json.Marshal(*h)
	if err != nil {
		return err
	}
	cmd := shared.CommandMsg{
		Command: "heartbeat",
		Body:    string(body),
	}
	cmdJson, err := json.Marshal(cmd)
	if err != nil {
		return err
	}
	err = ch.Publish("", serverName, false, false, amqp.Publishing{
		Body: []byte(cmdJson),
	})
	return err
}

func SendChatmsg(ch *amqp.Channel, msg *shared.ChatMsg) error {
	body, err := json.Marshal(*msg)
	if err != nil {
		return err
	}
	cmd := shared.CommandMsg{
		Command: "chatmsg",
		Body:    string(body),
	}
	cmdJson, err := json.Marshal(cmd)
	if err != nil {
		return err
	}
	err = ch.Publish("", serverName, false, false, amqp.Publishing{
		Body: []byte(cmdJson),
	})
	return err
}
